//
// Created by LONG on 2020/9/26.
//

#include <time.h>
#include <sys/time.h>
#include "../../include/utils/logApi.h"
#include "../../include/utils/misc.h"
#include "myVsprintf.h"

static const char *gCspLogLvlTable[] =
{
    "D",
    "I",
    "W",
    "E",
};
STATIC_ASSERT(ARRAY_SIZE(gCspLogLvlTable) == LOG_LVL_BUTT);

STATIC_ASSERT(LOG_PARAM_SIZE_MAX >= sizeof(uint64_t) * CSP_LOG_ARG_NUM_MAX);

S_CspLogInst *gCspLogInst = NULL;
uint32_t gCspLogLevel = LOG_LVL_BUTT;

int32_t LOG_Init()
{
    gCspLogInst = OS_MemCalloc(1, sizeof(S_CspLogInst));
    if (NULL == gCspLogInst)
    {
        printf("LOG_Init() OS_MemCalloc() error!\n");
        return CSP_RC_ER;
    }

    gCspLogInst->appTypeStr = "";

    gCspLogLevel = LOG_LVL_DBG;

    return CSP_RC_OK;
}

int32_t LOG_Final()
{
    gCspLogLevel = LOG_LVL_BUTT;

    OS_MemFree(gCspLogInst);

    return CSP_RC_OK;
}

void LOG_UpdateTick(uint64_t tick)
{
    if (gCspLogInst)
    {
        gCspLogInst->tick = tick;
    }
}

void LOG_SetLogLevel(uint32_t lvl)
{
    gCspLogLevel = lvl;
}

void LOG_SetAppType(const char *appTypeStr)
{
    if (gCspLogInst)
    {
        gCspLogInst->appTypeStr = appTypeStr;
    }
}

void LOG_SetModuleNameTable(const char *moduleName[], uint32_t size, uint32_t prefixLen)
{
    if (gCspLogInst)
    {
        gCspLogInst->prefixLen = prefixLen;
        gCspLogInst->tableSize = size;
        gCspLogInst->moduleNameTable = moduleName;
    }
}

void CSP_LogProcess(S_CspLogItem *pLogItem)
{
    struct tm tm;
    uint32_t offset;
    uint32_t copySize;
    const char *appTypeStr;
    const char *moduleName;
    char buffer[LOG_PARAM_SIZE_MAX * 4];

    offset = strftime(buffer, sizeof(buffer), "[%y-%m-%d %X", localtime_r(&pLogItem->tv.tv_sec, &tm));
    if (0 == offset)
    {
        return;
    }

    appTypeStr = gCspLogInst->appTypeStr ? gCspLogInst->appTypeStr : "XXXXXX";

    moduleName = "XXXXXX";
    if (pLogItem->moduleId < gCspLogInst->tableSize)
    {
        moduleName = gCspLogInst->moduleNameTable[pLogItem->moduleId] + gCspLogInst->prefixLen;
    }

    offset += snprintf(buffer + offset, sizeof(buffer) - offset, ".%03ld][%.4x][%1.1s][%2.2s:%-6.6s] ",
        pLogItem->tv.tv_usec / 1000, gCspLogInst->tick & 0xFFFF, gCspLogLvlTable[pLogItem->logLevel],
        appTypeStr, moduleName);
    if (offset >= sizeof(buffer))
    {
        return;
    }

    if (!pLogItem->isMem)
    {
        if (!pLogItem->syncInd)
        {
            my_va_list ap;
            my_va_start(ap, pLogItem->params);
            rpl_vsnprintf(buffer + offset, sizeof(buffer) - offset, pLogItem->fmtStr, ap);
            my_va_end(ap);
        }
        else
        {
            copySize = MIN(pLogItem->paramSize, sizeof(buffer) - offset);
            memcpy(buffer + offset, pLogItem->params, copySize);
            offset += copySize;
            buffer[offset] = '\0';
        }
    }
    else
    {
        buffer[offset - 1] = '\n';
        CSP_Bin2Hex(pLogItem->params, pLogItem->paramSize, buffer + offset, sizeof(buffer) - offset);
    }

    printf("%s", buffer);
}

void CSP_LogWrite(uint32_t md, uint32_t lvl, uint32_t argc, uint32_t syncInd, const char *fmt, uint64_t value[])
{
    S_CspLogItem logItem;

    logItem.moduleId = md;
    logItem.logLevel = lvl;
    logItem.isMem = FALSE;
    logItem.fmtStr = fmt;
    logItem.syncInd = syncInd;
    gettimeofday(&logItem.tv, NULL);

    if (!syncInd)
    {
        logItem.paramSize = argc * sizeof(uint64_t);
        memcpy(logItem.params, value, logItem.paramSize);
    }
    else
    {
        my_va_list ap;
        my_va_start(ap, value);
        logItem.paramSize = rpl_vsnprintf(logItem.params, LOG_PARAM_SIZE_MAX, fmt, ap);
        my_va_end(ap);
    }
    CSP_LogProcess(&logItem);
}

void CSP_LogWriteMem(uint32_t md, uint32_t lvl, void *ptr, uint32_t size)
{
    S_CspLogItem logItem;

    logItem.moduleId = md;
    logItem.logLevel = lvl;
    logItem.isMem = TRUE;
    logItem.syncInd = TRUE;
    gettimeofday(&logItem.tv, NULL);

    logItem.paramSize = MIN(LOG_PARAM_SIZE_MAX, size);
    memcpy(logItem.params, ptr, logItem.paramSize);

    CSP_LogProcess(&logItem);
}
